package com.lookme.lmtool.utils;

import org.apache.commons.beanutils.ConversionException;

/**
 * 运算工具类，解析字符串运算式
 */
public final class CalculationUtils {
    /**
     * 表达式必须是boolean类型的，否则报错
     * 支持的比较符号 == >= > <= <
     * @param calculationString
     * @return
     * @version
     * @throws Exception
     */
    public static Boolean getBoolean(String calculationString) throws Exception {
        calculationString = format(calculationString);
        Object res = getObject(calculationString);
        if(res != null && res instanceof Boolean) {
            return (Boolean)res;
        }
        return null;
    }
    public static Double getDouble(String calculationString) throws Exception {
        calculationString = format(calculationString);
        Object res = getObject(calculationString);
        if(res != null && res instanceof Double) {
            return (Double)res;
        }
        return null;
    }

    public static Double getDouble(String calculationString,int scale) throws Exception {
        calculationString = format(calculationString);
        Object res = getObject(calculationString);
        if(res != null && res instanceof Double) {
            return round((Double)res,scale);
        }
        return null;
    }

    /**
     * 根据表达式类型，返回Double或Boolean
     * @param calculationString
     * @return
     * @version
     * @throws Exception
     */
    public static Object getResult(String calculationString) throws Exception {
        calculationString = format(calculationString);
        return getObject(calculationString);

    }

    public static Integer getInt(String calculationString) throws Exception {
        Object res;
        calculationString = format(calculationString);
        res = getObject(calculationString);
        if(res != null && res instanceof Double) {
            return ((Double)res).intValue();
        }
        return null;
    }


    public static double round(double v, int scale) {
        if (scale < 0 || scale > 10) {
            throw new IllegalArgumentException("scale参数必须介于[0,10]");
        }
        java.math.BigDecimal b = new java.math.BigDecimal(Double.toString(v));
        java.math.BigDecimal one = new java.math.BigDecimal("1");
        return b.divide(one, scale, java.math.BigDecimal.ROUND_HALF_UP).doubleValue();
    }

    /**
     * 格式预处理,去空格，回车等
     * @param calculationString
     * @return
     * @version
     */
    private static String format(String calculationString) {
        return calculationString.replaceAll("\\s*|\\t|\\r|\\n", "");
    }

    private static Object getObject(String calculationString ) throws Exception {
        check(calculationString);
        return getObject1(calculationString,false);
    }

    /**
     * 可能返回的类型，boolean，double，string
     * <p>递归去括号
     * @param calculationString
     * @return
     * @throws Exception
     * @version
     */
    private static Object getObject1(String calculationString,boolean isFirst) throws Exception{
        int a = calculationString.indexOf("(");
        if(a > -1) {
            String p1 = calculationString.substring(0,a);
            Object p2 = getObject1(calculationString.substring(a+1),true); // 递归字符串，该字符串去除第一个(,下一层会去掉对应的)
            if(p1.length()==0 && !(p2 instanceof String)) {
                return p2;
            }
            String newstr = p1+p2;
            if( (a = newstr.indexOf(")")) > -1 ) { // 递归结果还有(,表示还有括号可以递归
                return getObject1(newstr,true);
            }else { // 递归后没有括号了，直接返回计算结果
                return getSingleObject(newstr);
            }
        }else { // 获得并计算优先级最高的括号内容（里面没有括号了）
            int i1 = calculationString.indexOf(")");
            if(i1 > -1) { // 有) 时需要分割后运算,返回时去除第一个)
                if(!isFirst) {
                    throw new Exception("符号 ) 异常");
                }
                String h1 = calculationString.substring(0,i1);
                String h2 =  calculationString.substring(i1+1);
                if(h2.length()>0) {
                    return getSingleObject(h1)+h2; // )后面还有算式字符串p2，运算结果拼接p2，并返回
                }
                return getSingleObject(h1); // 直接返回运算结果，类型为boolean或double。
            }else {
                if(isFirst) {
                    throw new Exception("符号 ( 异常");
                }
            }
            return getSingleObject(calculationString);  // 直接返回运算结果，类型为boolean或double
        }
    }

    /**
     * 计算无括号的运算式,可能返回boolean、double
     * <p>计算的表达式如：1+3-6*2/2 == 3 或 1+3-6*2/2
     * @param calculationString
     * @return
     * @version
     */
    private static Object getSingleObject(String calculationString) throws ConversionException{
        int cou = 0;
        String regx = null;
        if(calculationString.indexOf(">=")>-1) {
            cou++;regx = ">=";
        }else if(calculationString.indexOf(">")>-1) {
            cou++;regx = ">";
        }
        if(calculationString.indexOf("<=")>-1) {
            cou++;regx = "<=";
        }else if(calculationString.indexOf("<")>-1) {
            cou++;regx = "<";
        }
        if(calculationString.indexOf("==")>-1) {
            cou++;regx = "==";
        }
        switch (cou) {
            case 0: // 返回数字
                return computeNumber(calculationString);
            case 1: // 返回boolean
                String[] two = calculationString.split(regx);
                if(two == null || two.length != 2) {
                    System.out.println("错误的格式1："+calculationString);
                    return null;
                }
                return compareBoolean(computeNumber(two[0]),regx,computeNumber(two[1]));
            default: // 错误的格式
                System.out.println("错误的格式："+calculationString);
                return null;
        }
    }

    /**
     * 计算无括号算式，如 1+2*5-8 。只返回null或double。算式异常时返回null
     * @param a
     * @exception Exception :表达式错误异常
     * @exception NumberFormatException 除数0、数字转化异常
     * @return
     * @version
     */
    private static Double computeNumber(String a) throws ConversionException {
        if(a == null) {
            return null;
        }
        char[] chars = a.toCharArray();
        Double tem = null,m1=null;
        int f=0;
        char ty = 0,f1=0,f2=0; // 1+ 2- 3 * 4/
        for(int i=0;i<chars.length;i++) {
            char t = chars[i];
            if(t<42 || t== 44 || t > 57) {
                throw new ConversionException("出现了不应该出现的字符："+t);
            }
            ; // 只允许的符号   0至9 + - . / *
            if(t < 48 && t != 46 && i > 0 && chars[i-1] > 47 && chars[i-1] < 58) { // + - * /
                if(t=='-' || t=='+') {
                    if(m1 != null) {
                        Double ddd= Double.valueOf(String.valueOf(chars, f, i-f));
                        if(f1 > 0) {
                            tem =  computeNormal(f1,tem,computeNormal(f2,m1,ddd));
                        }else {
                            tem = computeNormal(f2,m1,ddd);
                        }
                        m1 = null;
                    }else if(f1=='-') {
                        tem =tem - Double.valueOf(String.valueOf(chars, f, i-f));
                    }else if (f1 == '+') {
                        tem = tem + Double.valueOf(String.valueOf(chars, f, i-f));
                    }else { // 首次
                        tem = Double.valueOf(String.valueOf(chars, f, i-f));
                    }
                    f1 = t;
                }else {
                    if(m1 != null) {
                        m1 = computeNormal(f2,m1,Double.valueOf(String.valueOf(chars, f, i-f)));
                    }else {
                        m1 = Double.valueOf(String.valueOf(chars, f, i-f));
                    }
                    f2 = t;
                }
                ty = t;
                f = i + 1;
            }
            if(i == chars.length-1) {
                if(i-f+1 == 0) {
                    throw new ConversionException("错误的表达式:"+chars[i]); // 表示最后一个是字符
                }
                Double ddd= Double.valueOf(String.valueOf(chars, f,  i-f+1));
                if(f == 0) {
                    return ddd;
                }
                if(m1 != null) {
                    if(f1 > 0) {
                        return computeNormal(f1,tem,computeNormal(f2,m1,ddd));
                    }
                    return computeNormal(f2,m1,ddd);
                }
                return computeNormal(ty,tem,ddd);
            }
        }
        throw new ConversionException("错误的表达式");
    }

    /**
     * 检查括号
     * @param expr
     * @return
     * @version
     */
    private static void check(String expr) {
        int a=0,b=0;
        for(int i=0; i< expr.length();i++) {
            if('(' == expr.charAt(i)) {
                a ++;
            }else if(')' == expr.charAt(i)) {
                b ++;
            }
        }
        if(a != b) {
            throw new ConversionException("括号  异常");
        }
    }

    /**
     * 比较两个double数据大小,需要传入字符型的 “==” , ">=" , ">" 等
     * @param v1
     * @param tag
     * @param v2
     * @return
     * @version
     */
    private static Boolean compareBoolean(Double v1,String tag,Double v2) {
        if("==".equals(tag)) {
            return v1.equals(v2);
        }else if(">=".equals(tag)) {
            return v1 >= v2;
        }else if("<=".equals(tag)) {
            return v1 <= v2;
        }else if(">".equals(tag)) {
            return v1 > v2;
        }else if("<".equals(tag)) {
            return v1 < v2;
        }else {
            return false;
        }
    }

    private static Double computeNormal(char sign,Double d1,Double d2) {
        if(d1==null || d2== null) {
            return null;
        }
        switch(sign) {
            case '-':
                return d1 - d2;
            case '+':
                return d1 + d2;
            case '*':
                return d1 * d2;
            case '/':
                if(d2 == 0) {
                    throw new NumberFormatException("除数不能为0");
                }
                return d1 / d2;
            default :
                return null;
        }
    }
}